﻿//
// This example demonstrates how to read a series of dicom images
// and how to scroll with the mousewheel or the up/down keys
// through all slices
//
// some standard vtk headers
#include <vtkSmartPointer.h>
#include <vtkObjectFactory.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>
#include <vtkActor.h>
// headers needed for this example
#include <vtkImageViewer2.h>
#include <vtkDICOMImageReader.h>
#include <vtkInteractorStyleImage.h>
#include <vtkActor2D.h>
#include <vtkTextProperty.h>
#include <vtkTextMapper.h>
#include <vtkImageData.h>
#include <vtkPolyDataMapper.h>
#include <vtkMarchingCubes.h>
#include <vtkProperty.h>
#include <vtkSphereSource.h>

// needed to easily convert int to std::string
#include <sstream>
#include <stack>

// opencv
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>

#include "MeBlobDector.h"


using namespace cv;
using namespace std;

// helper class to format slice status message
class StatusMessage {
public:
    static std::string Format(int slice, int maxSlice) {
        std::stringstream tmp;
        tmp << "Slice Number  " << slice + 1 << "/" << maxSlice + 1;
        return tmp.str();
    }
};


// Define own interaction style
class myVtkInteractorStyleImage : public vtkInteractorStyleImage
{
public:
    static myVtkInteractorStyleImage* New();
    vtkTypeMacro(myVtkInteractorStyleImage, vtkInteractorStyleImage);
    
protected:
    vtkImageViewer2* _ImageViewer;
    vtkTextMapper* _StatusMapper;
    int _Slice;
    int _MinSlice;
    int _MaxSlice;
    
public:
    void SetImageViewer(vtkImageViewer2* imageViewer) {
        _ImageViewer = imageViewer;
        _MinSlice = imageViewer->GetSliceMin();
        _MaxSlice = imageViewer->GetSliceMax();
        _Slice = _MinSlice;
        cout << "Slicer: Min = " << _MinSlice << ", Max = " << _MaxSlice << std::endl;
    }
    
    void SetStatusMapper(vtkTextMapper* statusMapper) {
        _StatusMapper = statusMapper;
    }
    
    
protected:
    void MoveSliceForward() {
        if(_Slice < _MaxSlice) {
            _Slice += 1;
            cout << "MoveSliceForward::Slice = " << _Slice << std::endl;
            _ImageViewer->SetSlice(_Slice);
            std::string msg = StatusMessage::Format(_Slice, _MaxSlice);
            _StatusMapper->SetInput(msg.c_str());
            _ImageViewer->Render();
        }
    }
    
    void MoveSliceBackward() {
        if(_Slice > _MinSlice) {
            _Slice -= 1;
            cout << "MoveSliceBackward::Slice = " << _Slice << std::endl;
            _ImageViewer->SetSlice(_Slice);
            std::string msg = StatusMessage::Format(_Slice, _MaxSlice);
            _StatusMapper->SetInput(msg.c_str());
            _ImageViewer->Render();
        }
    }
    
    
    virtual void OnKeyDown() {
        std::string key = this->GetInteractor()->GetKeySym();
        if(key.compare("Up") == 0) {
            //cout << "Up arrow key was pressed." << endl;
            MoveSliceForward();
        }
        else if(key.compare("Down") == 0) {
            //cout << "Down arrow key was pressed." << endl;
            MoveSliceBackward();
        }
        // forward event
        vtkInteractorStyleImage::OnKeyDown();
    }
    
    
    virtual void OnMouseWheelForward() {
        //std::cout << "Scrolled mouse wheel forward." << std::endl;
        MoveSliceForward();
        // don't forward events, otherwise the image will be zoomed
        // in case another interactorstyle is used (e.g. trackballstyle, ...)
        // vtkInteractorStyleImage::OnMouseWheelForward();
    }
    
    
    virtual void OnMouseWheelBackward() {
        //std::cout << "Scrolled mouse wheel backward." << std::endl;
        if(_Slice > _MinSlice) {
            MoveSliceBackward();
        }
        // don't forward events, otherwise the image will be zoomed
        // in case another interactorstyle is used (e.g. trackballstyle, ...)
        // vtkInteractorStyleImage::OnMouseWheelBackward();
    }
    
    virtual void OnMouseMove() {
        // forward event
        vtkInteractorStyleImage::OnMouseMove();
    }
};

vtkStandardNewMacro(myVtkInteractorStyleImage);



void showDicom(cv::Mat I, int winLevel, int winWidth)
{
    double maxx=0,minn=0;
    double *max = &maxx;
    double *min = &minn;
    //I.convertTo(I,CV_64FC1);
    //minMaxIdx(I,min,max);
    
    minn = winLevel - winWidth/2.0;
    maxx = winLevel + winWidth/2.0;
    for(int i=0;i<I.rows;i++)
    {
        double * ptr = I.ptr<double>(i);
        //cout << i << ":";
        for(int j=0;j<I.cols;j++)
        {
            if (I.at<double>(i,j) < minn) {
                I.at<double>(i,j) = 0;
            } else if (I.at<double>(i,j) > maxx) {
                I.at<double>(i,j) = 255;
            } else {
                I.at<double>(i,j) = 255*(I.at<double>(i,j)-minn)*1/(maxx-minn);
            }
            //I.at<double>(i,j) = 255*(I.at<double>(i,j)-minn)/winWidth;
            //cout << *(ptr + j) << ", ";
        }
        //cout << endl;
    }

    imshow("DICOM Image",I);
}



/// 二值化图像并计算坐标
static vector<Point3f> convertImage(short *pData, int dims[3], double spacing[3])
{
    short sThreshold = 2250;

    MeBlobParams blobParams;
    blobParams.threshold = sThreshold;
    blobParams.minArea = 10;
    blobParams.maxArea = 3500;
    blobParams.filterByCircularity= true;
    blobParams.minCircularity = 0.4;
    blobParams.level = sThreshold;
    vector<Point3f> points;
    MeBlobDector::computeBlob3DPos(pData, dims, spacing, blobParams, points);
    
    cout << "Compute blob:" << endl;
    for (int i = 0; i<points.size(); i++) {
        cout << points[i] << endl;
    }
    return points;
}

/// 显示立体图
static void renderVolume(vtkSmartPointer<vtkDICOMImageReader> &reader, vector<Point3f> &points)
{
    vtkSmartPointer<vtkImageData> volume = vtkSmartPointer<vtkImageData>::New();
    double isoValue = 0.5;
    volume->DeepCopy(reader->GetOutput());
    
    vtkSmartPointer<vtkMarchingCubes> surface =
      vtkSmartPointer<vtkMarchingCubes>::New();
    surface->SetInputData(volume);
    surface->ComputeNormalsOn();
    surface->SetValue(0, isoValue);
    //surface->Print(cout);
  
    vtkSmartPointer<vtkRenderer> renderer =
      vtkSmartPointer<vtkRenderer>::New();
    renderer->SetBackground(.1, .2, .3);
  
    vtkSmartPointer<vtkRenderWindow> renderWindow =
      vtkSmartPointer<vtkRenderWindow>::New();
    renderWindow->AddRenderer(renderer);
    vtkSmartPointer<vtkRenderWindowInteractor> interactor =
      vtkSmartPointer<vtkRenderWindowInteractor>::New();
    interactor->SetRenderWindow(renderWindow);
  
    vtkSmartPointer<vtkPolyDataMapper> mapper =
      vtkSmartPointer<vtkPolyDataMapper>::New();
    mapper->SetInputConnection(surface->GetOutputPort());
    mapper->ScalarVisibilityOff();
  
    vtkSmartPointer<vtkActor> actor =
      vtkSmartPointer<vtkActor>::New();
    actor->SetMapper(mapper);
    double bounds[6];
    actor->GetBounds(bounds);
    cout << "bounds:" << bounds[0] << ", " << bounds[1] << ", " << bounds[2] << ", "
         << bounds[3] << ", " << bounds[4] << ", " << bounds[5] << endl;
    
    
    renderer->AddActor(actor);
    
    
    // 修改一些属性
    actor->GetProperty()->SetOpacity(0.5);
    
    // 加入点
    for(int i= 0; i < points.size(); i++) {
        vtkSmartPointer<vtkSphereSource> sphereSource = vtkSmartPointer<vtkSphereSource>::New();
    
        vtkSmartPointer<vtkPolyDataMapper> mapperB = vtkSmartPointer<vtkPolyDataMapper>::New();
        mapperB->SetInputConnection(sphereSource->GetOutputPort());
        vtkSmartPointer<vtkActor> actorB = vtkSmartPointer<vtkActor>::New();
        actorB->SetMapper(mapperB);
        actorB->SetPosition(points[i].x, points[i].y, points[i].z);
        actorB->SetScale(6);
        actorB->GetProperty()->SetColor(0.0, 1.0, 0.0);
        renderer->AddActor(actorB);
    }
  
    renderWindow->Render();
    interactor->Start();
}

/// 显示2维图
void renderImages(vtkSmartPointer<vtkDICOMImageReader> &reader)
{
    // Visualize
    vtkSmartPointer<vtkImageViewer2> imageViewer =
            vtkSmartPointer<vtkImageViewer2>::New();
    imageViewer->SetInputConnection(reader->GetOutputPort());
    
    double window = imageViewer->GetColorWindow();
    double level = imageViewer->GetColorLevel();
    cout << "window: " << window << " level: " << level << endl;
    
    // slice status message
    vtkSmartPointer<vtkTextProperty> sliceTextProp = vtkSmartPointer<vtkTextProperty>::New();
    sliceTextProp->SetFontFamilyToCourier();
    sliceTextProp->SetFontSize(20);
    sliceTextProp->SetVerticalJustificationToBottom();
    sliceTextProp->SetJustificationToLeft();
    
    vtkSmartPointer<vtkTextMapper> sliceTextMapper = vtkSmartPointer<vtkTextMapper>::New();
    std::string msg = StatusMessage::Format(imageViewer->GetSliceMin(), imageViewer->GetSliceMax());
    sliceTextMapper->SetInput(msg.c_str());
    sliceTextMapper->SetTextProperty(sliceTextProp);
    
    vtkSmartPointer<vtkActor2D> sliceTextActor = vtkSmartPointer<vtkActor2D>::New();
    sliceTextActor->SetMapper(sliceTextMapper);
    sliceTextActor->SetPosition(15, 10);
    
    // usage hint message
    vtkSmartPointer<vtkTextProperty> usageTextProp = vtkSmartPointer<vtkTextProperty>::New();
    usageTextProp->SetFontFamilyToCourier();
    usageTextProp->SetFontSize(14);
    usageTextProp->SetVerticalJustificationToTop();
    usageTextProp->SetJustificationToLeft();
    
    vtkSmartPointer<vtkTextMapper> usageTextMapper = vtkSmartPointer<vtkTextMapper>::New();
    usageTextMapper->SetInput("- Slice with mouse wheel\n  or Up/Down-Key\n- Zoom with pressed right\n  mouse button while dragging");
    usageTextMapper->SetTextProperty(usageTextProp);
    
    vtkSmartPointer<vtkActor2D> usageTextActor = vtkSmartPointer<vtkActor2D>::New();
    usageTextActor->SetMapper(usageTextMapper);
    usageTextActor->GetPositionCoordinate()->SetCoordinateSystemToNormalizedDisplay();
    usageTextActor->GetPositionCoordinate()->SetValue( 0.05, 0.95);
    
    // create an interactor with our own style (inherit from vtkInteractorStyleImage)
    // in order to catch mousewheel and key events
    vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
            vtkSmartPointer<vtkRenderWindowInteractor>::New();
    
    vtkSmartPointer<myVtkInteractorStyleImage> myInteractorStyle =
            vtkSmartPointer<myVtkInteractorStyleImage>::New();
    
    // make imageviewer2 and sliceTextMapper visible to our interactorstyle
    // to enable slice status message updates when scrolling through the slices
    myInteractorStyle->SetImageViewer(imageViewer);
    myInteractorStyle->SetStatusMapper(sliceTextMapper);
    
    imageViewer->SetupInteractor(renderWindowInteractor);
    // make the interactor use our own interactorstyle
    // cause SetupInteractor() is defining it's own default interatorstyle
    // this must be called after SetupInteractor()
    renderWindowInteractor->SetInteractorStyle(myInteractorStyle);
    // add slice status message and usage hint message to the renderer
    imageViewer->GetRenderer()->AddActor2D(sliceTextActor);
    imageViewer->GetRenderer()->AddActor2D(usageTextActor);
    
    // initialize rendering and interaction
    //imageViewer->GetRenderWindow()->SetSize(400, 300);
    //imageViewer->GetRenderer()->SetBackground(0.2, 0.3, 0.4);
    imageViewer->Render();
    imageViewer->GetRenderer()->ResetCamera();
    imageViewer->Render();
    renderWindowInteractor->Start();
}

int main(int argc, char* argv[])
{
    // Verify input arguments
    if ( argc != 2 )
    {
        std::cout << "Usage: " << argv[0]
                  << " FolderName" << std::endl;
        return EXIT_FAILURE;
    }
    
    std::string folder = argv[1];
    //std::string folder = "C:\\VTK\\vtkdata-5.8.0\\Data\\DicomTestImages";
    
    // Read all the DICOM files in the specified directory.
    vtkSmartPointer<vtkDICOMImageReader> reader =
            vtkSmartPointer<vtkDICOMImageReader>::New();
    reader->SetDirectoryName(folder.c_str());
    reader->Update();
    cout << "DICOM inforamtion:" << endl;
    cout << "reader[w, h]: " << reader->GetWidth() << ", " << reader->GetHeight() << endl;
    float rescaleSlope = reader->GetRescaleSlope();
    cout << "rescaleSlope: " << rescaleSlope << endl;
    float rescaleOffset = reader->GetRescaleOffset();
    cout << "rescaleOffset: " << rescaleOffset << endl;
    
    //reader->Print(cout);
    
    vtkImageData* volume = reader->GetOutput();
    
    double origin[3];
    volume->GetOrigin(origin);
    cout << "origin: " << origin[0] << ", " << origin[1] << ", " << origin[2] << endl;
    double spacing[3];
    volume->GetSpacing(spacing);
    cout << "spacing: " << spacing[0] << ", " << spacing[1] << ", " << spacing[2] << endl;
    int dim[3];
    volume->GetDimensions(dim);
    cout << "dimensions: " << dim[0] << ", " << dim[1] << ", " << dim[2] << endl;
    int extent[6];
    volume->GetExtent(extent);
    cout << "extent: " << extent[0] << ", " << extent[1] << ", " << extent[2] << ", " <<
            extent[3] << ", " << extent[4] << ", " << extent[5] << endl; 

    // 获取原图像数据的灰度值范围
    // 如：CT值的单位是Hounsfield，简称为Hu，范围是-1024-3071。用于衡量人体组织对X射线的吸收率，设定水的吸收率为0Hu。
    double range[2];
    volume->GetScalarRange(range); 
    cout << "range: " << range[0] << ", " << range[1] << endl;
    cout << "GetScalarTypeAsString (): " << volume->GetScalarTypeAsString () << endl;
        
    //图像的像素值
    short * pData = (short *)volume->GetScalarPointer();
    vector<Point3f> points = convertImage(pData, dim, spacing);
    
    
    // 显示
    bool bShow2D = false;
    if (bShow2D)
        renderImages(reader);
    else {
        renderVolume(reader, points);
    }
    
    
    return EXIT_SUCCESS;
}
